import processing.core.*;

/**
 * 
 * 
 *
 */
public class Matrix{
	/** The parent applet that draws the modeled matrix.*/
	private PApplet parent;
	/** The images that are used to create the matrix.*/
	private ImageCatalog catalog;
	/** The two dimensional array (=matrix) that stores the images in the right order.*/
	private ProfileImage[][] matrix;
	/** The size of the canvas in pixels.*/
	private int canvasSize;
	/** The number of pictures on the side of the matrix at start.*/
	private int initSize;
	
	private int zoomStartX;
	private int zoomStartY;
	private int zoomStartSizePixels;
	private PImage zoomStartAreaImage;
	
	private int zoomCurrentSizePixels;
	private int zoomCurrentSide;
	private int zoomCurrentSize;
	private int zoomCurrentCol;
	private int zoomCurrentRow;
	private int zoomCurrentX;
	private int zoomCurrentY;
	
	private int zoomTargetSize;
	private int zoomTargetCol;
	private int zoomTargetRow;
	
	private int zoomLevelSteps;
	private int zoomLevel;
	
	/**
	 * 
	 * @param parent - the parent window where matrix is drawn
	 * @param catalog - the <code>ImageCatalog</code> the matrix is based on
	 */
	public Matrix(PApplet parent, ImageCatalog catalog) {
		this.parent = parent;
		this.catalog = catalog;
		this.zoomLevel = 0;
		this.canvasSize = parent.height;
	}
	
	public int getSize(){
		return this.matrix.length;
	}
	
	/**
	 * Creates Matrix from a rect of image drawn in parent PApplet.
	 * @param size
	 * @param rectX
	 * @param rectY
	 * @param rectSize
	 */
	public void createMatrix(int size, int rectX, int rectY, int rectSize){
		this.initSize = size;
		this.zoomCurrentSize = size;
		this.zoomCurrentSide = canvasSize/this.zoomCurrentSize;
		
		// the matrix is created, a two dimensional array of ProfileImages
		this.matrix = new ProfileImage[size][size];
		
		int col = 0;
		int row = 0;
		int rectSide = rectSize/this.initSize;
		
		ColorCalculator c_calc = new ColorCalculator(this.parent);
		
		for (int i = 0; (i < size*size); i++){

			PImage replace = parent.get(rectX+col*rectSide,rectY+row*rectSide,rectSide,rectSide);
			int color = c_calc.calculateColor(replace);
			
			ProfileImage user = catalog.getBestImage(color);
			this.matrix[row][col] = user;
			
			// Calculate row and col for NEXT image
			col++;
			if (col == this.initSize){
				col = 0;
				row++;
			}
		}
	}
	
	/**
	 * Draws matrix
	 */
	public void drawMatrix(){
		
		if(this.zoomLevel != 0){
			
			this.zoomCurrentSizePixels += (this.canvasSize-this.zoomStartSizePixels)/this.zoomLevelSteps;
			this.zoomCurrentX -= this.zoomStartX/this.zoomLevelSteps;
			this.zoomCurrentY -= this.zoomStartY/this.zoomLevelSteps;
			
			if(this.zoomLevel == this.zoomLevelSteps) {
				this.zoomLevel = 0;
			} else {
				this.zoomLevel++;
			}
		} else {
			this.zoomCurrentSizePixels = this.canvasSize;
			this.zoomCurrentX = 0;
			this.zoomCurrentY = 0;
		}
		
		this.zoomCurrentSide = this.zoomCurrentSizePixels/this.zoomCurrentSize;
		
		// If we are zooming, let's print scaled up image of start position
		
		if (this.zoomLevel > 0) {
			//System.out.println("zoomCurrentSize: "+ zoomCurrentSize + " zoomCurrentSizePixels: "+ zoomCurrentSizePixels  +" zoomCurrentSide: " + zoomCurrentSide + " zoomCurrentX: " + zoomCurrentX + " zoomCurrentY: " + zoomCurrentY + " zoomCurrentRow: " + zoomCurrentRow + " zoomCurrentCol: " + zoomCurrentCol);
			this.parent.image(this.zoomStartAreaImage,this.zoomCurrentX,this.zoomCurrentY, this.zoomCurrentSizePixels, this.zoomCurrentSizePixels);
		} 
		
		// Otherwise draw the matrix cell by cell
		
		else {
			for (int col = 0; (col < this.zoomCurrentSize); col++){
				for (int row = 0; (row < this.zoomCurrentSize); row++){
					int x = this.zoomCurrentX + col*this.zoomCurrentSide;
					int y = this.zoomCurrentY + row*this.zoomCurrentSide;
					this.parent.image(matrix[row+this.zoomCurrentRow][col+this.zoomCurrentCol].getPic_square(), x, y, this.zoomCurrentSide, this.zoomCurrentSide);
				}
			}
		}
	}

	
	/**
	 * Set the properties of matrix to draw. x and y coordinate represents the upper left 
	 * corner from the previous matrix from which the next matrix is created.
	 * 
	 * @param drawSize	the number of images on one side of the matrix
	 * @param x	the starting x coordinate for the matrix
	 * @param y	the starting y coordinate for the matrix
	 * TODO comments
	 */
	public void setZoomSize(int zoomTargetSize, int zoomStartX, int zoomStartY, int zoomStartSizePixels, int zoomLevelSteps){
		
		this.zoomStartX = zoomStartX;
		this.zoomStartY = zoomStartY;
		this.zoomStartSizePixels = zoomStartSizePixels;
		this.zoomStartAreaImage = this.parent.get(zoomStartX,zoomStartY,zoomStartSizePixels,zoomStartSizePixels);
		
		this.zoomTargetSize = zoomTargetSize;
		
		if (this.zoomTargetSize == this.initSize) {
			this.zoomTargetCol = 0;
			this.zoomTargetRow = 0;
		} else {
			this.zoomTargetCol += (zoomStartX/this.zoomCurrentSide);
			this.zoomTargetRow += (zoomStartY/this.zoomCurrentSide);
		}
		
		this.zoomCurrentSize = this.zoomTargetSize;
		this.zoomCurrentSizePixels = zoomStartSizePixels;
		this.zoomCurrentX = zoomStartX;
		this.zoomCurrentY = zoomStartY;
		this.zoomCurrentRow = this.zoomTargetRow;
		this.zoomCurrentCol = this.zoomTargetCol;
		
		this.zoomLevelSteps = zoomLevelSteps;
		
		if (this.zoomLevelSteps > 0) {
			this.zoomLevel = 1;
		}
	}

	public boolean isZooming(){
		if (this.zoomLevel > 0) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Returns the user from the matrix specified by the given coordinates.
	 * 
	 * @param x the x coordinate of the image counted from the top left corner of the matrix
	 * @param y the y coordinate of the image counted from the top left corner of the matrix
	 * @return returns the specified <code>ProfileImage</code>
	 */
	public ProfileImage getUser(int x, int y){
		int col = this.zoomCurrentCol+(x/this.zoomCurrentSide);
		int row = this.zoomCurrentRow+(y/this.zoomCurrentSide);
		return this.matrix[row][col];
	}

}
